#ifndef __SOCKET_H__
#define __SOCKET_H__

#ifdef _WRS_KERNEL
# define VXWORKS
#endif


#ifdef VXWORKS
#  include <inetLib.h>
#  include <sockLib.h>
#elif (defined _WIN32)
#  include <winsock2.h>
#  include <windows.h>
#  define ssize_t   SSIZE_T
#  define socklen_t unsigned long
#else
#  include <netinet/in.h>
#  include <arpa/inet.h>
#endif


/**
 * Wraps Socket Addresses for improved portability.
 *
**/
struct eSocketAddress
{
  eSocketAddress (void) {
    address.sin_family = AF_INET;
    size               = sizeof (sockaddr_in);
  }

  void setAddress (const char* szHost);

  void setAddressAny (void) {
    address.sin_addr.s_addr = INADDR_ANY;
  }

  void setPort (int port) {
    address.sin_port = htons (port);
  }

  unsigned short getPort (void) const {
    return ntohs (address.sin_port);
  }

  const char* getAddress (void) const {
    if (address.sin_addr.s_addr == INADDR_ANY)
      return "(*)";
    else
      return inet_ntoa (address.sin_addr);
  }

  sockaddr_in address;
  socklen_t   size;
};

/**
 * Enumerates all of the supported Socket Domains.
 *
**/
enum eSocketDomain {
  IPv4,
  IPv6,     /* Unimplemented. */
  UNIX      /* Unimplemented. */
};

/**
 * Enumerates all of the supported Socket Types. 
 *
**/
enum eSocketType {
  Stream,
  Datagram, /* Unimplemented. */
  Raw       /* Unimplemented. */
};

/**
 * Enumerates all of the supported Socket Protocols.
 *
**/
enum eSocketProtocol {
  TCP = 6,
  UDP = 17  /* Unimplemented. */
};

/**
 * This class wraps BSD Sockets.
 *
 *   It is designed to keep the underlying implementation hidden,
 *     which will be important if Epsilon HTTP is ever ported to
 *       Microsoft Windows.
 *
**/
class eSocket {
public:
  /* For Internal Use ONLY */
  eSocket ( int             handle,
            eSocketAddress  addr,
            eSocketDomain   domain   = IPv4,
            eSocketType     type     = Stream,
            eSocketProtocol protocol = TCP );

  eSocket ( eSocketDomain   domain   = IPv4,
            eSocketType     type     = Stream,
            eSocketProtocol protocol = TCP ) {
    domain_   = domain;
    type_     = type;
    protocol_ = protocol;

    int domain_enum   = 0;
    int type_enum     = 0;
    int protocol_enum = 0;

    switch (domain_) {
      case IPv4:
        domain_enum = PF_INET;
        break;
      default:
        /* Unsupported ... */
        break;
    }

    switch (type_) {
      case Stream:
        type_enum = SOCK_STREAM;
        break;
      case Datagram:
        type_enum = SOCK_DGRAM;
        break;
      default:
        /* Unsupported ... */
        break;
    }

    switch (protocol_) {
      case TCP:
      case UDP:
        protocol_enum = protocol_;
        break;
      default:
        /* Unsupported ... */
        break;
    }

#ifdef _WIN32
  static WSADATA wsaData;
  static bool    initialized = false;

  if (initialized == false) {
    /* Initialize Winsock 2.2 */
    int rc = WSAStartup (MAKEWORD (2, 2), &wsaData);

    if (rc != 0) {
      //printf ("WSAStartup failed: %d\n", rc);
    } else {
      initialized = true;
    }
  }
#endif

    handle_ = socket (domain_enum, type_enum, protocol_enum);
  }

  virtual ~eSocket (void);

  bool    bind    (eSocketAddress addr);
  bool    connect (eSocketAddress addr);
  bool    close   (void);

  /* Assuming this socket is "connected". (e.g. only TCP) */
  ssize_t send    (const void* data,   size_t length);
  ssize_t recv    (void*       buffer, size_t max_len);


  /* Exposes part of the class that really should not be,
       future work should be done to eliminate the need for this. */
  int getHandle (void) { return handle_; }

  const eSocketAddress& getAddress (void) const { return address_; }

  static int getErrorCode (int ret_code);

protected:
  eSocketAddress  address_;
  int             handle_; /* Operating System Handle */
  eSocketDomain   domain_;
  eSocketType     type_;
  eSocketProtocol protocol_;
};

#endif /* __SOCKET_H__ */